Spring笔记(六)—— IOC 容器之不同的配置方式

基于注解的配置

XML 设置的替代方法是基于注释的配置,它依赖于字节码元数据来连接组件而不是角括号声明。开发人员通过使用相关类,方法或字段声明上的注释来将配置移动到组件类本身中,而不是使用 XML 来描述 bean 布线。例如,Spring2.0 引入了使用 @Required 注释实现 required 属性的可能性。Spring2.5 添加了对 JSR-250 注释的支持,例如 @PostConstruct 和 @PreDestroy。Spring3.0 增加了对 javax.inject 包中包含的 JSR-330(Java的依赖注入)注释的支持,例如 @Inject 和 @Named。

@required

@Required 注释适用于 bean 属性的 setter 方法,此注释表示被标注的 bean 属性必须在配置时通过 bean 定义中的显式属性值或通过自动装配被填充,如果被标注的 bean 属性未被填充,容器将抛出异常。如下例子所示:

1
2
3
4
5
6
7
8
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}

@Autowired

Spring 通过 @Autowired 注解实现 Bean 的依赖注入,可以运用在多种自动注入场景。

应用于构造函数

Spring 框架4.3,如果目标 bean 只定义了一个构造函数,则可以不需要 @Autowired。如果有多个构造函数,则至少必须为其中一个声明注解,告知容器哪一个是必须被使用的。

1
2
3
4
5
6
7
8
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}

应用于 setter 方法

1
2
3
4
5
6
7
8
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}

应用于拥有参数的某个方法

1
2
3
4
5
6
7
8
9
10
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog, CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}

应用于字段或与构造函数混合使用

1
2
3
4
5
6
7
8
9
10
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
private MovieCatalog movieCatalog;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}

应用于数组或集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MovieRecommender {
@Autowired
private MovieCatalog[] movieCatalogs;
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
private Map<String, MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
this.movieCatalogs = movieCatalogs;
}
// ...
}

应用于泛型

原始类:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}

泛型类:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
public class MyConfiguration {
@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;
}

required 属性

如果容器中没有一个和标注变量类型匹配的 Bean,Spring 容器启动时会抛出异常,默认情况下,required 属性值为 true,即要求一定要找到匹配的 Bean。

1
2
3
4
5
6
7
8
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired(required=false)
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}

@Primary

因为按类型自动装配可能导致多个候选项,所以通常需要对选择过程有更多的控制。@Primary 表示当多个 bean 自动装配到单值依赖关系的候选项时,应该优先选择 @Primary 指定的 bean。如果候选项中只有一个 primary bean,那么它将作为自动装配的值。
如下定义 firstMovieCatalog 作为 primary MovieCatalog:

1
2
3
4
5
6
7
8
9
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}

@Qualifier

@Qualifier 可以将限定符值与特定参数相关联,缩小类型匹配集,以便为每个参数选择特定的 bean。如果容器中有一个以上匹配的 Bean 时,则可以通过 @Qualifier 注解限定 Bean 的名称。

1
2
3
4
5
6
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}

@Qualifier 也可以用于独立的构造函数参数或方法参数:

1
2
3
4
5
6
7
8
9
10
11
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main")MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}

@Resource

@Resource 要求提供一个 Bean 名称的属性,如果没有明确指定名称,则默认名称派生自字段名称或 setter 方法。在字段的情况下,它采用字段名称; 在 setter 方法的情况下,它接受 bean 属性名称。

1
2
3
4
5
6
7
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}

以下示例使用名称为 movieFinder 的 bean 注入到其 setter 方法中:

1
2
3
4
5
6
7
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}

@Autowired 默认按类型匹配注入 Bean,@Resource 则按名称匹配注入 Bean,@Inject 和 @Autowired 一样按类型匹配注入 Bean,只不过它没有 required 属性。

@PostConstruct 和 @PreDestroy

使用 <bean> 进行配置时,可以通过 init-method 和 destory-method 属性指定 Bean 的初始化及容器销毁前执行的方法。Spring 从 2.5 开始支持 JSR-250 中定义的 @PostConstruct 和 @PreDestroy 注解,在 Spring 中相当于 init-method 和 destory-method 属性的功能,不过使用注解时,可以在一个 Bean 中定义多个 @PostConstruct 和 @PreDestroy 方法。

1
2
3
4
5
6
7
8
9
10
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}

基于 Java 类的配置

@Bean 注解用于表示方法实例化,配置和初始化一个由 Spring IoC 容器管理的新对象。@Bean 注释与 <bean/> 元素具有相同的作用。@Configuration 注解表示被标注的 POJO 类可以为 Spring 容器提供 Bean 定义的信息。
简单的实例如下:

1
2
3
4
5
6
7
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}

与 AppConfig 类等同的 XML 配置:

1
2
3
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

@Bean

@Bean 是一个方法级别的注解和 XML <bean/> 元素的替代,它支持 init-method, destroy-method, autowiring 和 name 等 <bean/> 提供的属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Foo {
public void init() {
// initialization logic
}
}
public class Bar {
public void cleanup() {
// destruction logic
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public Foo foo() {
return new Foo();
}
@Bean(destroyMethod = "cleanup")
public Bar bar() {
return new Bar();
}
}

@Scope

用于指定 Bean 的作用域。

1
2
3
4
5
6
7
8
@Configuration
public class MyConfiguration {
@Bean
@Scope("prototype")
public Encryptor encryptor() {
// ...
}
}

自定义 Bean 的名称,别名和描述

Bean 可以通过 name 属性自定义名称,当有多个名称时,其它的为别名;还可以使用 @Description 定义 Bean 的描述信息。

1
2
3
4
5
6
7
8
9
@Configuration
public class AppConfig {
@Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
@Description("Provides a basic example of a bean")
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
return new DataSource();
}
}

@Configuration

@Configuration 是一个类级别的注解,表示该类是为 Spring 容器提供 bean 定义信息的类。

@Import

等同于 XML 配置中的 <import/> 元素,@Import 注解允许从另一个 Configuration 类中加载 @Bean 定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}

如上所述,ConfigB 中导入了 ConfigA,所以在实例化上下文的时候,只需要提供 ConfigB。

1
2
3
4
5
6
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);
// now both beans A and B will be available...
A a = ctx.getBean(A.class);
B b = ctx.getBean(B.class);
}

@ImportResource

用于加载资源文件路径的注解。

  • properties-config.xml

    1
    2
    3
    <beans>
    <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
    </beans>
  • jdbc.properties

    1
    2
    3
    jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
    jdbc.username=sa
    jdbc.password=
  • bean 类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Configuration
    @ImportResource("classpath:/com/acme/properties-config.xml")
    public class AppConfig {
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Bean
    public DataSource dataSource() {
    return new DriverManagerDataSource(url, username, password);
    }
    }
  • main 类

    1
    2
    3
    4
    5
    public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
    }

不同配置方式的比较

描述 基于 XML 配置 基于注解配置 基于 Java 类配置
Bean 定义 在 XML 文件中通过 <bean> 元素定义 Bean。如:<bean class="com.lake.UserDao"/> 在 Bean 实现类出通过标注 @Component 或衍型类(@Repository、@Service 及 @Controller)定义 Bean 在标注了 @Configuration 的 Java 类中,通过在类方法上标注 @Bean 定义一个 Bean,方法必须提供 Bean 的实例化逻辑
Bean 名称 通过 <bean> 的 id 或 name 属性定义 Bean。如:<bean id="userDao" class="com.lake.UserDao"/>,默认名称为:com.lake.UserDao#0 通过注解的 value 属性定义,如 @Component(“userDao”),默认名称为小写字母开头的类名(不带包名):userDao 通过 @Bean 的 name 属性定义,如 @Bean(“userDao”),默认名称为方法名
Bean 注入 通过 <property> 子元素或通过 p 命名空间的动态属性,如 p:userDao-ref=”userDao” 进行注入 通过在成员变更或方法入参处标注 @Autowired,按类型匹配自动注入,还可以配合使用 @Qualifier 按名称匹配方式注入 比较灵活,可以通过在方法处通过 @Autowired 使方法入参绑定 Bean,然后在方法中通过代码进行注入,还可通过调用配置类的 @Bean 方法进行注入
Bean 作用域 通过 <bean> 的 scpoe 属性指定,如:<bean class="com.lake.UserDao" scope="prototype"/> 通过在类定义处标注 @Scope 指定,如:@Scope(“prototype”) 通过在 Bean 方法定义处标注 @Scope 指定
Bean 生命周期方法 通过 <bean> 的 init-method 和 destroy-method 属性指定 Bean 实现类的方法名,最多只能指定一个初始化方法和销毁方法 通过在目标方法上标注 @PostConstruct 和 @PreDestory 注解指定初始化或销毁方法,可以定义任意多个 通过 @Bean 的 initMethod 或 destoryMethod 指定一个初始化或销毁方法。对于初始化方法来说,可以直接在方法内部通过代码的方式灵活定义初始化逻辑
Bean 延迟初始化 通过 <bean> 的 lazy-init 属性指定,默认为 default,继承自 <beans> 的 default-lazy-init 设置,该值默认为 false 通过在类定义出标注 @Lazy 指定,如:@Lazy(true) 通过在 Bean 方法定义出标注 @Lazy 指定
适合场景 Bean 实现类来源于第三方类库,如 DataSource、JdbcTemplate 等,因无法在类中标注注解,通过 XML 配置方式较好;命名空间的配置,如 aop、context 等,只能采用基于 XML 的配置 Bean 的实现类时当前项目开发的,可以直接在 Java 类中使用基于注解的配置 优势在于可以通过代码的方式控制 Bean 初始化的整体逻辑,所以如果实例化 Bean 的逻辑比较复杂,则比较适合用基于 Java 类配置的方式 |

参考资料:

Spring 3.x 企业应用开发实战
Spring Framework Reference Documentation

热评文章